/** @file
@brief Contains declarations related to #PrynChange, a modification of state.
*/
#ifndef PRYN_CHANGE_H
#define PRYN_CHANGE_H

#include <pryn/platform.h>

typedef struct PrynChange PrynChange; ///< A description of a change in state.
typedef enum PrynChangeType PrynChangeType; ///< The type of a change in state.

#include <pryn/monitor.h>
#include <pryn/object.h>
#include <pryn/result.h>
#include <pryn/state.h>

/** @defgroup PrynChange PrynChange C API
	@{ */

/// A type of change in state.
enum PrynChangeType
{
	PrynChangeTypeNone = 0, ///< An illegal value used to indicate uninitialised state.

	PrynChangeTypeAll = 3, ///< (class) All changes inherit from this type.

	PrynChangeTypeComponent = 4, ///< (class) Anything related to a component.

	PrynChangeTypePin = 2, ///< (class) Anything related to a pin.
	PrynChangeTypePinChangeValue = 1, ///< A pin's value has been set or changed.

	// next value: 5
};

PrynImport (PrynResult, prynChangeClear, (PrynChange *change, PrynChangeType type, const PrynObject *object))
	/**< @brief Clear a change structure's parameters. */

PrynImport (PrynResult, prynChangeDispatch, (PrynChange *change))
	/**< @brief Send the change to the object and to the parent scopes until it reaches the state. */

PrynImport (PrynResult, prynChangeDispatchSimple, (PrynChangeType type, const PrynObject *object))
	/**< @brief Create and dispatch a simple change. */

PrynImport (PrynResult, prynChangeType, (PrynChange *change, PrynChangeType *output))
	/**< @brief Retrieve the type of a change.

	@param change The change to retrieve the type of.
	@param[out] output Receives the type of the change, or #PrynChangeTypeNone on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynChangeState, (PrynChange *change, PrynState **output))
	/**< @brief Retrieve the state the change exists within.

	@param change The change to retrieve the state of.
	@param[out] output Receives the type of the change, or #PrynChangeTypeNone on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynChangeMonitor, (PrynChange *change, PrynMonitor **output))
	/**< @brief Return the monitor for a change.

	The result depends upon what is currently receiving the change notification.
	
	@param change The change to retrieve the monitor of.
	@param[out] output Receives the monitor, or 0 on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynChangeObject, (PrynChange *change, PrynObject *output))
	/**< @brief Return the object that has been changed.

	@param change The change to retrieve the object of.
	@param[out] output Receives the object, or an empty object on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

PrynImport (PrynResult, prynChangeTypeMatch, (PrynChangeType type, PrynChangeType match))
	/**< @brief Return whether the change @e type is equal to @e match or any root class of @e match.

	@param type The change type to match.
	@param match The change type to attempt to match it against, or any root class of it.
	@returns #PrynResultTrue if they match, #PrynResultFalse if they don't, or an error code; see #PrynResult. */

PrynImport (PrynResult, prynChangeTypeAbstract, (PrynChangeType type, PrynChangeType *output))
	/**< @brief Retrieve the super-class change type, or #PrynChangeTypeNone if done (also returning #PrynResultFalse).

	@param type The change type to get the super-class of.
	@param output Written with the parent change type or #PrynChangeTypeNone if there is none.
	@returns #PrynResultTrue if a parent type was found, #PrynResultFalse if it wasn't, or #PrynResultUnknownEnumeration if the @e type is an unknown value. */

#ifdef PrynInternalStructs
/** @brief A description of a change in state.

	@ingroup PrynChange */
struct PrynChange
{
#ifdef PrynInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.

	PrynState *pState; ///< The state this change is occuring within.
	PrynMonitor *pMonitor; ///< The monitor the notification function was bound to.
	PrynObject pObject; ///< The object that has changed.
	PrynChangeType pType; ///< What has changed.

/** @} */
#endif /* PrynInternal */

#ifdef __cplusplus
	static inline void DispatchSimple (PrynChangeType type, const PrynObject &object) { prynCheckErrorNull (prynChangeDispatchSimple (type, &object)); } ///< Create and dispatch a simple change with no necessary additional arguments.

	inline void clear (PrynChangeType type, const PrynObject &object) { checkError (prynChangeClear (this, type, &object)); } ///< Clear the change structure's parameters.

	inline void dispatch () { checkError (prynChangeDispatch (this)); } ///< Send the change to the object and the parent scopes until it reaches the state.

	inline PrynChangeType type () { PrynChangeType output; checkError (prynChangeType (this, &output)); return output; }
		/**< @brief Return the type of a change. */

	inline PrynState *state () { PrynState *output; prynCheckErrorNull (prynChangeState (this, &output)); return output; }
		/**< @brief Return the state of the change. */

	inline PrynMonitor *monitor () { PrynMonitor *result; checkError (prynChangeMonitor (this, &result)); return result; }
		/**< @brief Return the monitor for a change.

		The result depends upon what is currently receiving the change notification. */

	inline PrynObject object () { PrynObject result; checkError (prynChangeObject (this, &result)); return result; }
		/**< @brief Return the object that has been changed. */

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.
#endif /* __cplusplus */
};
#endif /* PrynInternal */

/** @} */

#endif /* PRYN_CHANGE_H */
